home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
prog
/
asm_n_z.arj
/
SPY'.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-05-11
|
17KB
|
430 lines
name spyprime
rt equ 0dh ; return
lf equ 0ah ; linefeed
eof equ 1ah ; end of file
ok_seg equ 1 ; flag for segment decode check
ok_off equ 2 ; flag for offset decode check
max_size equ 4095 ; maximum size for SPY' search region
beep_delay equ 4 ; number of clock ticks between beeps
timer_count equ 597 ; frequency of beep = 1,193,180 / timer_count
; These constants are used by the newint16h routine in this, the SPY' program.
; They are not needed for the SPY program.
ckey1 equ 20cdh ; value of first key (a "backwords" int 20)
ckey2 equ 1234h ; value of second key
ckey3 equ 5678h ; value of third key
code segment
assume cs:code, ds:code
org 0
key1 dw ? ; offset of int 20h instruction
; used to verify a PSP location
org 100h
begin: jmp start ; jump to program start location
;-------------------------------------------------------------------------------
; This copyright message appears when SPY'.COM is "typed" to the screen.
;-------------------------------------------------------------------------------
db 8,8,8,' '
copyright db rt,lf
db "SPY' Copyright (C) 1987 by Charles Lazo III, v1.0"
db eof,8,' ',rt,lf,'$'
key2 dw ckey2 ; keys used to determine prior load of SPY'
key3 dw ckey3
oldint8 dd ? ; double word storage for old int 8 vector
oldint16h dd ? ; double word storage for old int 16 vector
beeps dw 0 ; number of beeps (notices) to send to user
delay dw 1 ; number of clock ticks 'til next beep
beep_on db 0 ; beep on/off status
spy_size dw ? ; number of bytes to spy on
resident db 0 ; indicates prior load of SPY' (0 = none)
spy_location label dword
spy_off dw ? ; storage for value of offset for SPYing
spy_seg dw ? ; storage for value of segment for SPYing
;-------------------------------------------------------------------------------
; Comparisons of the spied region and the spy buffer (set up initially as a copy
; of the spied region) are made in this addition to the clock interrupt.
; Changes to the spied region are counted and the spy buffer is updated. The
; speaker is turned on and off here, but frequency is set during program
; initialization.
;-------------------------------------------------------------------------------
newint8 proc near
push ax ; save interrupted program's registers
push bx
push cx
push si
push di
push ds
push es
mov ax,cs ; our data lies here
mov ds,ax
lea si,spy_buffer ; point to our buffer
les di,spy_location ; point to spied region
mov cx,spy_size ; compare whole region
cld ; forward!
cmp_more: repz cmpsb ; compare until no match or cx = 0
jz cmp_done ; if zero, then cx = 0 and we're done
inc beeps ; account for a change
mov al,es:[di-1] ; change accounted; update spy_buffer
mov [si-1],al
or cx,cx ; set zero flag by cx (avoid inf loop)
jmp short cmp_more ; continue 'til done
cmp_done: cmp beep_on,0 ; is the beep on?
jz do_beep? ; no, shall we do a beep?
dec beep_on ; yes, turn it off
in al,97 ; get speaker control bits
and al,0fch ; set them off
out 97,al ; turn off speaker
jmp short exit ; job done; get out
do_beep?: cmp beeps,0 ; are there beeps to be done?
jz delay1 ; no, get out
dec delay ; reduce delay 'til next beep
jnz exit ; not zero, then exit
in al,97 ; get speaker control bits
or al,3 ; set them on
out 97,al ; turn on speaker
inc beep_on ; signal beep is on
mov delay,beep_delay; reinitialize delay counter
dec beeps ; one less beep to do
jmp short exit ; leave now
delay1: mov delay,1 ; don't wait for first beep of a series
exit: pop es ; restore registers
pop ds
pop di
pop si
pop cx
pop bx
pop ax
jmp dword ptr cs:oldint8 ; continue with interrupt
newint8 endp
;-------------------------------------------------------------------------------
; Keyboard interrupt, int 16h, is issued with function number ah = 77h in the
; set_es routine. If there has been no prior load of SPY', then the interrupt
; returns without changing anything. However, if SPY' has been run previously,
; then this routine has been chained into the interrupt 16h handler and the
; issue of "keyboard" function number 77h causes the returned value of es to be
; what cs was for the original load of SPY'.
;-------------------------------------------------------------------------------
newint16h proc near
cmp ah,77h ; our cue?
jne not_us ; nah, not us
push ax ; save this
mov ax,ckey1 ; have we a match for the first key?
cmp ax,cs:key1
je test_2nd ; yes, test the 2nd key
jmp short onward ; no previous load found
test_2nd: mov ax,ckey2 ; have we a match for the second key?
cmp ax,cs:key2
je test_3rd ; yes, test the 3rd key
jmp short onward ; no previous load found
test_3rd: mov ax,ckey3 ; have we a match for the third key?
cmp ax,cs:key3
jne onward ; no match => no previous load
pop ax ; unstack saved value
mov ax,cs ; pass this cs in es
mov es,ax
mov ax,ckey1 ; pass these to inform set_es
mov bx,ckey2
iret
onward: pop ax ; recover this
not_us: jmp dword ptr cs:oldint16h ; continue with interrupt
newint16h endp
;-------------------------------------------------------------------------------
; Here the region spied upon is copied to an area of the TSR that remains in
; memory after termination, the spy_buffer. The es register is equal either to
; cs if SPY' has not yet been loaded or to the value of cs when SPY' was loaded
; previously (accomplished by a call to the set_es routine).
;-------------------------------------------------------------------------------
copy_spied: lea di,spy_buffer ; destination of copy
mov si,spy_off ; the offset is source of copy
mov cx,spy_size ; number of bytes to copy
mov ax,spy_seg ; load segment of SPY' region to ds
push ds ; SOD (save our data)
mov ds,ax
cld ; forward copy
rep movsb ; copy the SPY' region to spy_buffer
pop ds ; RestoreOD
cmp resident,0 ; is SPY' currently resident in memory?
je tsr ; no, make it so
mov ax,4c00h ; yes, end with error code 0
int 21h
;-------------------------------------------------------------------------------
; SPY' has not yet been loaded into memory, so we do it now. First the 8253-5
; chip that generates the frequency of the beeps is initialized with the value
; given by the constant timer_count. Then the new interrupt 8 routine is
; spliced in and finally the constant max_size is used to reserve enough memory
; for the largest permissible spy buffer.
;-------------------------------------------------------------------------------
tsr: mov delay,beep_delay; initialize delay counter
; set 8253-5 programmable timer to chosen frequency
mov al,182 ; byte to initialize 8253 timer
out 67,al ; tell timer next two bytes are count
mov ax,timer_count ; get timer count
out 66,al ; output low byte
mov al,ah
out 66,al ; output high byte
mov ax,3508h ; get the interrupt 8 (clock) vector
int 21h ; with DOS function 35h call
; Retain offset and segment of interrupt 8 vector:
mov word ptr cs:oldint8,bx
mov word ptr cs:oldint8+2,es
lea dx,newint8 ; place offset in dx
mov ax,2508h ; set its pointer into interrupt table
int 21h ; with DOS function 25h call
mov ax,3516h ; get the int 16h (keyboard) vector
int 21h ; with DOS function 35h call
; Retain offset and segment of interrupt 16h vector:
mov word ptr cs:oldint16h,bx
mov word ptr cs:oldint16h+2,es
lea dx,newint16h ; place offset in dx
mov ax,2516h ; set its pointer into interrupt table
int 21h ; with DOS function 25h call
lea dx,spy_buffer ; where SPY' buffer begins
mov cx,max_size ; add the maximum size of the buffer:
add dx,cx
mov cl,4 ; compute number of paragraphs to save:
shr dx,cl ; bytes to paragraphs in dx
inc dx ; insure sufficient size for buffer
mov ax,3100h ; terminate but stay resident code = 0
int 21h
;-------------------------------------------------------------------------------
; The following is initialization code and data that is overwritten by the data
; copied to the spy buffer when this buffer is initialized with a copy of the
; spied region.
;-------------------------------------------------------------------------------
spy_buffer: ; this is where SPY' will store a copy of the SPY' region
; to later check for any changes to the region
syntax db rt,lf,"SPY' syntax: SPY' xxxx:yyyy L zzz",rt,lf,rt,lf
db 'Where xxxx, and yyyy are hexadecimal numbers up to '
db 'four digits in length and',rt,lf
db "zzz is a one to three digit hexadecimal number. SPY' "
db 'will monitor the segment-',rt,lf
db 'offset region xxxx:yyyy for a length of zzz bytes and '
db 'report to the user with a',rt,lf
db 'number of beeps equal to the number of bytes changed '
db 'in that region if and when',rt,lf
db 'any are changed.',rt,lf,'$'
progress db 0 ; flags for progress of command line conversion
;-------------------------------------------------------------------------------
; This routine will convert an ASCII hex digit in al (with either upper or lower
; case alpha hex) into a 1-of-16 binary value in al.
;-------------------------------------------------------------------------------
make_binary proc near ; hex ASCII digit in al to binary in al
cmp al,'0' ; less than "0", then not hex digit
jb not_digit
cmp al,'f' ; greater than "f", then not hex digit
ja not_digit
cmp al,'9' ; test for numeric digit
ja not_numeric ; not a numeric digit
sub al,'0' ; convert to binary
ret
not_numeric: cmp al,'F' ; over "F"?
ja low_case ; yes, test for lower case
cmp al,'A' ; less than "A"?
jb not_digit ; yes, not hex digit
sub al,37h ; convert to binary
ret
low_case: cmp al,'a' ; less than "a"?
jb not_digit ; yes, not hex digit
sub al,57h ; convert to binary
ret
not_digit: stc ; set carry flag if not hex digit
ret
make_binary endp
;-------------------------------------------------------------------------------
; This routine is called to allow the command line parse routines to ignore any
; space and tab characters on the command line that do not lie in hex numbers.
; If the return character at the end of the command line is encountered, then
; the carry flag is set; otherwise it is reset.
;-------------------------------------------------------------------------------
skip proc near ; skip over spaces and tabs; signal
; a RETURN with the carry flag
more_skip: cmp al,' ' ; is it a space?
je another ; if so, get another
cmp al,9 ; is it a tab?
je another ; if so, get another
cmp al,rt ; is it a RETURN?
je a_rt ; yes, set carry and return
clc ; not one of these three so return
ret ; with carry clear
another: lodsb ; get another character
jmp short more_skip ; and try again
a_rt: stc ; return with carry set
ret
skip endp
;-------------------------------------------------------------------------------
; Here we parse the command line, which by the conditions of proper syntax has
; the three hexadecimal values for segment, offset and byte size for the region
; which is to be spied. The routine between cnv_more: and cnv_error: is run
; once each to decode segment, offset and size which are stored respectively in
; the variables spy_seg, spy_off, and spy_size unless a syntax error is found.
; The variable progress is used to keep track of which of these three numbers
; has just been decoded. A semicolon can be used to preface comments at the end
; of the command line.
;-------------------------------------------------------------------------------
convert proc near ; convert hex ASCII to binary in ax
mov cx,16 ; for hex to binary conversion
cnv_more: xor bx,bx ; accumulate result here
xor ah,ah ; need this zero too
dec si ; move back to last character
cycle: lodsb ; get possible ASCII hex digit
call make_binary ; if ASCII hex, then make binary in al
jc test_seg ; if carry set, then char not hex digit
xchg ax,bx ; accumulation in ax, last digit in bx
mul cx ; bump to next power
or dx,dx ; test for overflow
jnz cnv_error ; result can't be larger than fff0h
add ax,bx ; add in most recent digit
xchg ax,bx ; accumulation in bx, ah = 0
jmp short cycle ; continue conversion
cnv_error: pop ax ; remove return location (of call)
jmp stax ; display syntax and end
test_seg: test progress,ok_seg ; have we decoded the segment value?
jnz test_off ; yes, test offset
call skip ; skip over spaces and tabs
jc cnv_error ; carry flag set if RETURN was found
cmp al,':' ; syntax says must have colon here
jne cnv_error
lodsb ; get another
call skip ; skip over spaces and tabs
jc cnv_error ; carry flag set if RETURN was found
mov spy_seg,bx ; store value of segment for SPYing
or progress,ok_seg ; segment value is now decoded
jmp short cnv_more ; continue to convert offset and size
test_off: test progress,ok_off ; have we decoded the offset value?
jnz test_size ; yes, test size
call skip ; skip over spaces and tabs
jc cnv_error ; carry flag set if RETURN was found
and al,0dfh ; convert "L" or "l" to "L"
cmp al,'L' ; syntax says must have "l" or "L" here
jne cnv_error
lodsb ; get another
call skip ; skip over spaces and tabs
jc cnv_error ; carry flag set if return was found
mov spy_off,bx ; store value of offset for SPYing
or progress,ok_off ; offset value is now decoded
jmp short cnv_more ; continue to convert size
test_size: cmp bx,max_size ; size must not be over maximum
ja cnv_error
call skip ; skip over spaces and tabs
jnc comment ; shall allow comments at cmd line end
cnv_finished: mov spy_size,bx ; store away size
ret
comment: cmp al,';' ; test for comment symbol
jne cnv_error ; not comment symbol, then error
jmp short cnv_finished ; all done
convert endp
;-------------------------------------------------------------------------------
; This is the routine that contains the code that this program was designed to
; demonstrate. It serves to detect if SPY' has been loaded previously as a TSR.
; If it has, then the code segment at the time of the previous load will be put
; into es; if not, then es will return with the current value of cs. The method
; used attempts to find the set of key values key1, key2, and key3 in the memory
; above the current PSP. key1 is word cd20h which is the int 20h instruction
; found at offset 0 of every PSP. If key1 matches, then key2 is tried and if it
; matches key3 is tried. A triple match gives fair confidence that SPY' has
; been run as a TSR once before. It is even possible to gain further confidence
; or detect the presence of a different version of the same program by comparing
; a string in the returned es to a string in cs, say the copyright notice. Note
; that the keys are compared in the newint16h routine that handles the function
; 77h if SPY' has been previously loaded.
;-------------------------------------------------------------------------------
set_es proc near ; es becomes cs of original SPY' load
mov ah,77h ; function to find previous load address
int 16h
cmp ax,ckey1 ; if ax = ckey1
jne not_loaded
cmp bx,ckey2 ; and bx = ckey2
jne not_loaded
mov resident,1 ; then there's been a prior load
ret
not_loaded: mov ax,cs ; else, return with es = cs
mov es,ax
ret
set_es endp
;-------------------------------------------------------------------------------
; Execution starts here. The command line is decoded, new parameters are passed
; to the prior load if it exists, and the beeps variable is set to zero for a
; fresh start. Execution is then passed to copy_spied: and other termination
; code that can not be overwritten by the copy process.
;-------------------------------------------------------------------------------
start: lea dx,copyright ; display copyright
mov ah,9
int 21h
mov bx,80h ; get command line byte count
mov al,[bx]
cmp al,1
ja parse ; if command given, parse it
stax: lea dx,syntax ; display command syntax
mov ah,9
int 21h
mov ax,4c01h ; and exit with error code 1
int 21h
parse: mov si,82h ; address first usable character
lodsb
call skip ; skip over spaces and tabs
jc stax ; carry is set if RETURN was found
call convert ; convert and store hex values
call set_es ; set es to spy_buffer segment
mov ax,spy_seg ; set SPY' region to newly given values
mov es:spy_seg,ax ; (this isn't necessary for first
mov ax,spy_off ; time through, but it doesn't
mov es:spy_off,ax ; hurt either.)
mov ax,spy_size
mov es:spy_size,ax
xor ax,ax ; initialize beeps count
mov es:beeps,ax
jmp copy_spied ; copy that which is to be spied
code ends
end begin